package com.chenqq.camerademo.camera.helper;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbConfiguration;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;

import com.chenqq.camerademo.R;
import com.chenqq.camerademo.camera.bean.DeviceFilter;
import com.chenqq.camerademo.camera.bean.USBInfo;
import com.chenqq.camerademo.camera.bean.USBVendorId;
import com.chenqq.camerademo.camera.bean.UsbDeviceInfo;
import com.chenqq.camerademo.camera.field.Field_USB_Type;
import com.chenqq.camerademo.camera.receiver.USBReceiver;
import com.chenqq.camerademo.camera.usb.UsbControlBlock;
import com.chenqq.camerademo.camera.usbInterface.OnUsbDeviceReceiver;
import com.chenqq.camerademo.camera.usbInterface.OnUsbInfoListener;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import androidx.annotation.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.BehaviorSubject;

import static com.chenqq.camerademo.camera.field.Field_USB_Parameter.*;

/**
 * USB帮助类
 */
public class UsbHelperTools implements OnUsbDeviceReceiver {

    private UsbManager usbManager;
    private String Tag = getClass().getSimpleName();
    private PendingIntent mPermissionIntent = null;
    private ArrayList<DeviceFilter> deviceFiltersAll = new ArrayList<>();
    private ArrayList<USBInfo> usbDevices = new ArrayList<>();
    private BehaviorSubject<Object> usbBehaviorSubject;
    private OnUsbInfoListener onUsbInfoListener;
    private UsbHelperTools() {
    }

    private static UsbHelperTools inStance;

    public static UsbHelperTools getInStance() {
        if (inStance == null) {
            inStance = new UsbHelperTools();
        }
        return inStance;
    }

    /**
     * 初始化USB 管理局
     *
     * @param context
     */
    private void initUsbManager(Context context) {
        if (usbManager == null) {
            usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
        }
        if (mPermissionIntent == null) {
            mPermissionIntent = PendingIntent.getBroadcast(context, 0
                    , new Intent(USBReceiver.ACTION_USB_PERMISSION), 0);
        }
    }

    public UsbManager getUsbManager() {
        return usbManager;
    }

    public void setOnUsbInfoListener(OnUsbInfoListener onUsbInfoListener) {
        this.onUsbInfoListener = onUsbInfoListener;
    }

    /**
     * 注册USB接口监听
     *
     * @param context
     */
    public void registerUsbReceiver(Context context,int xml) {
        IntentFilter fiter = new IntentFilter();
        fiter.addAction(Intent.ACTION_MEDIA_MOUNTED);
        fiter.addAction(Intent.ACTION_MEDIA_EJECT);
        fiter.addDataScheme("file");
        context.registerReceiver(USBReceiver.getInstance(), fiter);
        IntentFilter filter1 = new IntentFilter();
        filter1.addAction(USBReceiver.ACTION_USB_PERMISSION);
        filter1.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        filter1.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        context.registerReceiver(USBReceiver.getInstance(), filter1);
        initUsbManager(context);
        getDeviceFilters(context, xml==0?R.xml.device_filter:xml);
    }

    private Handler usbHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case 1:
                    checkUsbDeviceList("");
                    break;
                case 2:
                    if (usbDevices.size() == 0) {
                        break;
                    }
                    USBInfo usbInfo = usbDevices.get(0);
                    boolean authorization = usbInfo.isAuthorization();
                    Message message = new Message();
                    message.obj = usbInfo;
                    if (authorization) {
                        //发送出去 开启
                        message.what = 3;
                        usbHandler.sendMessage(message);
                        break;
                    }
                    message.what = 4;
                    message.arg1 = usbInfo.getType();
                    usbHandler.sendMessage(message);
                    break;
                case 3:
                    USBInfo usbInfo2 = (USBInfo) msg.obj;
                    usbDevices.remove(usbInfo2);
                    openDeviceByUSBInfo(usbInfo2);
                    break;
                case 4:
                    USBInfo usbInfo1 = (USBInfo) msg.obj;
                    requestUSBDevicePermission(usbInfo1);
                    break;
            }
            return false;
        }
    });

    //开启USB设备
    private void openDeviceByUSBInfo(USBInfo usbInfo2) {
        Log.i(Tag, "openDeviceByUSBInfo: " + usbInfo2.isAuthorization());
        if (usbInfo2.isVideo()) {
            //表示是我的了
            Log.i(Tag, "openDeviceByUSBInfo:"+"11");
            CameraUnits.getInStance().openCameraUSBDevice(usbInfo2);
            return;
        }
        if (onUsbInfoListener != null) {
            onUsbInfoListener.openDevice(usbInfo2);
        }

    }

    /**
     * 检查USB设备
     */
    private void checkUsbDeviceList(Object object) {
        if (usbBehaviorSubject == null) {
            usbBehaviorSubject = BehaviorSubject.create();
            Disposable subscribe = usbBehaviorSubject.subscribeOn(Schedulers.io())
                    .observeOn(Schedulers.io())
                    .unsubscribeOn(Schedulers.io())
                    .map(new Function<Object, ArrayList<USBInfo>>() {
                        @Override
                        public ArrayList<USBInfo> apply(@NonNull Object object) throws Exception {
                            ArrayList<USBInfo> arrayList = new ArrayList<>();
                            ArrayList<UsbDevice> devices = new ArrayList<>();
                            if (object instanceof String) {
                                HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
                                devices.addAll(deviceList.values());
                            }
                            if (object instanceof UsbDevice) {
                                devices.add((UsbDevice) object);
                            }
                            for (UsbDevice device : devices) {
                                USBInfo info = checkDeviceType(device);
                                if (info.getType() == Field_USB_Type.USB_CLASS_PER_INTERFACE) {
                                    Log.i("检查USB", "检查到通用设备" + device.getDeviceName());
                                    continue;
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_HID) {
                                    Log.i("检查USB", "检查到鼠键设备" + device.getDeviceName());
                                    continue;
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_HUB) {
                                    Log.i("检查USB", "检查到集线器设备" + device.getDeviceName());
                                    continue;
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_MASS_STORAGE) {
                                    Log.i("检查USB", "检查到存储设备" + device.getDeviceName());
                                    continue;
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_AUDIO) {
                                    Log.i("检查USB", "检查到音频设备" + device.getDeviceName());
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_COMM) {
                                    Log.i("检查USB", "检查到串口设备" + device.getDeviceName());
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_PHYSICAL) {
                                    Log.i("检查USB", "检查到物理设备" + device.getDeviceName());
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_STILL_IMAGE) {
                                    Log.i("检查USB", "检查到相机📷" + device.getDeviceName());
                                    info.setVideo(true);
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_PRINTER) {
                                    Log.i("检查USB", "检查到打印机" + device.getDeviceName());
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_CDC_DATA) {
                                    Log.i("检查USB", "检查到疾病控制与预防中心的数据设备" + device.getDeviceName());
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_CSCID) {
                                    Log.i("检查USB", "检查到智能卡设备" + device.getDeviceName());
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_CONTENT_SEC) {
                                    Log.i("检查USB", "检查到安全设备" + device.getDeviceName());
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_VIDEO) {
                                    Log.i("检查USB", "检查到视频设备" + device.getDeviceName());
                                    info.setVideo(true);
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_PERSONAL_HEALTHCARE) {
                                    Log.i("检查USB", "检查到个人医疗设备" + device.getDeviceName());
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_DIDGNOSTIC_DEVICE) {
                                    Log.i("检查USB", "检查到USB2的设备" + device.getDeviceName());
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_WIRELESS_CONTROLLER) {
                                    Log.i("检查USB", "检查到无线控制器设备" + device.getDeviceName());
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_MISC) {
                                    Log.i("检查USB", "检查到其他设备" + device.getDeviceName());
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_APP_SPEC) {
                                    Log.i("检查USB", "检查到USB-IF设备" + device.getDeviceName());
                                }
                                if (info.getType() == Field_USB_Type.USB_CLASS_APP_SPEC) {
                                    Log.i("检查USB", "供应商特定设备" + device.getDeviceName());
                                }
                                Log.i("检查USB", "供应商特定设备" + device.toString());
                                if (deviceFiltersAll.size() > 0) {
                                    for (DeviceFilter filter : deviceFiltersAll) {
                                        if (filter.getVendorId()==device.getVendorId()&&
                                                filter.getProductId()==device.getVendorId()) {
                                            info.setVideo(true);
                                        }
                                    }
                                }
                                arrayList.add(info);
                            }
                            return arrayList;
                        }
                    }).subscribe(new Consumer<ArrayList<USBInfo>>() {
                        @Override
                        public void accept(ArrayList<USBInfo> arrayList) throws Exception {
                            usbDevices.addAll(arrayList);
                            if (usbDevices.size() == 0) {
                                Log.i("检查USB", "无设备需求USB申请权限");
                                return;
                            }
                            if (usbHandler.hasMessages(2)) {
                                usbHandler.removeMessages(2);
                            }
                            usbHandler.sendEmptyMessage(2);
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            Log.e(Tag, "检查USB设备失败：" + throwable.getMessage());
                            CameraTools.getInStance().removeDisposable(Tag);
                            usbBehaviorSubject = null;
                        }
                    });
            CameraTools.getInStance().addDisposable(Tag, subscribe);
        }
        usbBehaviorSubject.onNext(object);

    }
    //mVendorId=6790,mProductId=29987,mClass=255,mSubclass=0,mProtocol=0,mManufacturerName=null,mProductName=USB Serial
    //mVendorId=4292,mProductId=60000,mClass=0,mSubclass=0,mProtocol=0,mManufacturerName=Silicon Labs,mProductName=CP2102 USB to UART Bridge Controller
    //mVendorId=3034,mProductId=22599,mClass=239,mSubclass=2,mProtocol=1,mManufacturerName=RYS,mProductName=SY 1080P camera
    // mVendorId=1443,mProductId=37424,mClass=239,mSubclass=2 mProtocol=1,mManufacturerName=HD Camera Manufacturer,mProductName=USB 2.0 Camera
    /**
     * 获取USB设备的权限
     *
     * @param usbInfo
     */
    public void requestUSBDevicePermission(USBInfo usbInfo) {
        synchronized (this) {
            UsbDevice usbDevice = usbInfo.getUsbDevice();
            boolean b = usbManager.hasPermission(usbDevice);
            if (b) {
                onPermission(usbDevice, true);
            } else {
                usbManager.requestPermission(usbDevice, mPermissionIntent);
            }
        }
    }


    private USBInfo checkDeviceType(UsbDevice device) {
        USBInfo usbInfo = new USBInfo();
        if (device == null) {
            return usbInfo;
        }
        int interfaceCount = device.getInterfaceCount();
        if (interfaceCount == 0) {
            return usbInfo;
        }
        UsbInterface anInterface = device.getInterface(0);
        if (anInterface == null) {
            return usbInfo;
        }
        int interfaceClass = anInterface.getInterfaceClass();
        usbInfo.setType(interfaceClass);
        usbInfo.setUsbDevice(device);
        usbInfo.setAuthorization(usbManager.hasPermission(device));
        return usbInfo;
    }


    public void checkStart(){
        if (usbHandler.hasMessages(1)){
            usbHandler.removeMessages(1);
        }
        usbHandler.sendEmptyMessageDelayed(1, 500);
    }

    /**
     * 取消注册
     *
     * @param context
     */
    public void unRegisterUsbReceiver(Context context) {
        context.unregisterReceiver(USBReceiver.getInstance());
    }

    //USB设备插入
    @Override
    public void onAttach(UsbDevice usbDevice) {
        checkUsbDeviceList(usbDevice);

    }

    //USB设备拔出
    @Override
    public void onDettach(UsbDevice device) {
        USBInfo usbInfo = checkDeviceType(device);
        CameraUnits.getInStance().closeCameraUSBDevice(usbInfo);
        if (onUsbInfoListener != null) {
            onUsbInfoListener.closeDevice(usbInfo);
        }
    }

    //USB设备授权
    @Override
    public void onPermission(UsbDevice device, boolean isPermission) {
        Log.i(Tag, "Usb设备授权: " + device.getDeviceName() + " 成功？" + isPermission);
        USBInfo usbInfo = null;
        for (USBInfo info : usbDevices) {
            UsbDevice usbDevice = info.getUsbDevice();
            if (usbDevice.getDeviceName().equals(device.getDeviceName())
                    && usbDevice.getVendorId() == device.getVendorId() &&
                    usbDevice.getProductId() == device.getProductId() &&
                    usbDevice.getDeviceSubclass() == device.getDeviceSubclass() &&
                    usbDevice.getDeviceClass() == device.getDeviceClass()) {
                usbInfo = info;
            }
        }
        if (usbInfo != null) {
            usbDevices.remove(usbInfo);
        }
        checkUsbDeviceList(device);
    }

    //U插入
    @Override
    public void onMediaMounted(String path) {
        if (onUsbInfoListener != null) {
            onUsbInfoListener.scanSDCard(path);
        }

    }

    //U拔出
    @Override
    public void onMediaEJECT(String path) {
        if (onUsbInfoListener != null) {
            onUsbInfoListener.closeSDCard(path);
        }

    }

    /**
     * 转换USB设备信息
     * @param usbInfo
     * @return
     */
    public UsbDeviceInfo initDeviceInfo(UsbDevice usbInfo) {
        if (usbInfo == null || usbInfo == null) {
            return null;
        }
        UsbDeviceInfo info = new UsbDeviceInfo();
        if (Build.VERSION.SDK_INT >= 21) {
            info.manufacturer = usbInfo.getManufacturerName();
            info.product = usbInfo.getProductName();
            info.serial = usbInfo.getSerialNumber();
        }
        if (Build.VERSION.SDK_INT>=23){
            info.usb_version = usbInfo.getVersion();
        }
        if (getUsbManager().hasPermission(usbInfo)){
            UsbDeviceConnection connection = getUsbManager().openDevice(usbInfo);
            if (connection==null){
                return null;
            }
             byte[] desc = connection.getRawDescriptors();
            if (TextUtils.isEmpty(info.usb_version)) {
                info.usb_version = String.format("%x.%02x", ((int)desc[3] & 0xff), ((int)desc[2] & 0xff));
            }
            if (TextUtils.isEmpty(info.version)) {
                info.version = String.format("%x.%02x", ((int)desc[13] & 0xff), ((int)desc[12] & 0xff));
            }
            if (TextUtils.isEmpty(info.serial)) {
                info.serial = connection.getSerial();
            }
            final byte[] languages = new byte[256];
            int languageCount = 0;
            try {
                int result = connection.controlTransfer(
                        USB_REQ_STANDARD_DEVICE_GET, // USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE
                        USB_REQ_GET_DESCRIPTOR,
                        (USB_DT_STRING << 8), 0, languages, 256, 0);
                if (result > 0) {
                    languageCount = (result - 2) / 2;
                }
                if (languageCount > 0) {
                    if (TextUtils.isEmpty(info.manufacturer)) {
                        info.manufacturer = getString(connection, desc[14], languageCount, languages);
                    }
                    if (TextUtils.isEmpty(info.product)) {
                        info.product = getString(connection, desc[15], languageCount, languages);
                    }
                    if (TextUtils.isEmpty(info.serial)) {
                        info.serial = getString(connection, desc[16], languageCount, languages);
                    }
                }
            } finally {
                connection.close();
            }
            if (TextUtils.isEmpty(info.manufacturer)) {
                info.manufacturer = USBVendorId.vendorName(usbInfo.getVendorId());
            }
            if (TextUtils.isEmpty(info.manufacturer)) {
                info.manufacturer = String.format("%04x", usbInfo.getVendorId());
            }
            if (TextUtils.isEmpty(info.product)) {
                info.product = String.format("%04x", usbInfo.getProductId());
            }
        }
        return info;
    }

    private  String getString(final UsbDeviceConnection connection, final int id, final int languageCount, final byte[] languages) {
        final byte[] work = new byte[256];
        String result = null;
        for (int i = 1; i <= languageCount; i++) {
            int ret = connection.controlTransfer(
                    USB_REQ_STANDARD_DEVICE_GET, // USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE
                    USB_REQ_GET_DESCRIPTOR,
                    (USB_DT_STRING << 8) | id, languages[i], work, 256, 0);
            if ((ret > 2) && (work[0] == ret) && (work[1] == USB_DT_STRING)) {
                // skip first two bytes(bLength & bDescriptorType), and copy the rest to the string
                try {
                    result = new String(work, 2, ret - 2, "UTF-16LE");
                    if (!"Љ".equals(result)) {	// 変なゴミが返ってくる時がある
                        break;
                    } else {
                        result = null;
                    }
                } catch (final UnsupportedEncodingException e) {
                    // ignore
                }
            }
        }
        return result;
    }

    /**
     * 根据XML获取设备信息列表 用于匹配
     *
     * @param context
     * @param deviceFilterXmlId
     * @return
     */
    public void getDeviceFilters(final Context context, final int deviceFilterXmlId) {
        final XmlPullParser parser = context.getResources().getXml(deviceFilterXmlId);
        final List<DeviceFilter> deviceFilters = new ArrayList<DeviceFilter>();
        try {
            int eventType = parser.getEventType();
            while (eventType != XmlPullParser.END_DOCUMENT) {
                if (eventType == XmlPullParser.START_TAG) {
                    final DeviceFilter deviceFilter = XmparserTools.readEntryOne(context, parser);
                    if (deviceFilter != null) {
                        deviceFilters.add(deviceFilter);
                    }
                }
                eventType = parser.next();
            }
        } catch (final XmlPullParserException e) {
            Log.d(Tag, "XmlPullParserException", e);
        } catch (final IOException e) {
            Log.d(Tag, "IOException", e);
        }
        List<DeviceFilter> deviceFilters1 = Collections.unmodifiableList(deviceFilters);
        deviceFiltersAll.clear();
        deviceFiltersAll.addAll(deviceFilters1);
    }

}
